﻿/******************************************************************************
 * GLocalSearch.cs
 * 
 * This module implements the GWebSearch search class and related types.
 *  
 * Date:   12/2008
 * 
 * Copyright (c) 2009, Mark Betz 
 * 
 * All rights reserved. 
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met: 
 * 
 *   * Redistributions of source code must retain the above copyright 
 *     notice, this list of conditions and the following disclaimer. 
 *   * Redistributions in binary form must reproduce the above copyright 
 *     notice, this list of conditions and the following disclaimer in the 
 *     documentation and/or other materials provided with the distribution. 
 *   * Neither the name of the Author nor the names of contributors may be 
 *     used to endorse or promote products derived from this software
 *     without specific prior written permission. 
 *     
 * THIS SOFTWARE IS PROVIDED BY MARK BETZ ''AS IS'' AND ANY  EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
 * IN NO EVENT SHALL MARK BETZ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 * 
 */
using System;
using System.Globalization;

namespace GSearch
{
    /// <summary>
    /// Type of the event arguments to GLocalSearch events
    /// </summary>
    public class LocalSearchEventArgs : SearchEventArgs
    {
       /// <summary>
       /// Constructor used to initialize from a SearchEventArgs object.
       /// </summary>
       /// <param name="se"></param>
        public LocalSearchEventArgs(SearchEventArgs se)
            : base(se.Status, se.Result, se.Ex)
        {
        }

        /// <summary>
        /// Returns a reference to a <see cref="GSearch.GLocalResponse"/> object
        /// that contains the response, for certain event types
        /// </summary>
        public new GLocalResponse Result
        { get { return (GLocalResponse)(base.Result); } }
    }

    /// <summary>
    /// Used to set the center point of the search. Stores the longitude and
    /// latitude in decimal degrees. No checking is performed.
    /// </summary>
    public class Coordinate
    {
        /// <summary>
        /// Default constructor for Coordinate class
        /// </summary>
        public Coordinate()
        {
            Latitude = 0;
            Longitude = 0;
        }
        
        /// <summary>
        /// A System.Double containing a latitude in decimal degrees.
        /// </summary>
        public double Latitude { get; set; }
        
        /// <summary>
        /// A System.Double containing a longitude in decimal degrees.
        /// </summary>
        public double Longitude { get; set; }
    }

    /// <summary>
    /// Used to specify the bounding rect for the sspn param. See Google
    /// API documentation for how to interact with a map object to 
    /// obtain a bounding box calculation.
    /// </summary>
    public class BoundingRect
    {
        /// <summary>
        /// Default constructor for BoundingRect class
        /// </summary>
        public BoundingRect()
        {
            Term1 = 0;
            Term2 = 0;
        }
        
        /// <summary>
        /// A System.Double containing the first term of a bounding box
        /// </summary>
        public double Term1 { get; set; }

        /// <summary>
        /// A System.Double containing the second term of a bounding box
        /// </summary>
        public double Term2 { get; set; }
    }

    /// <summary>
    /// Enumerates the types of results available. The default is set to
    /// LocalOnly, which returns just local results. KmlOnly returns 
    /// Kml entries. There are some issues with the results from this type
    /// of search and you may see json deserialization failures. Blended
    /// returns both types of result.
    /// </summary>
    public enum SearchLocalResults
    {
        /// <summary>
        /// Selects both local and kml result types
        /// </summary>
        Blended,
        
        /// <summary>
        /// Selects only kml result types
        /// </summary>
        KmlOnly,
        
        /// <summary>
        /// Selects only local result types
        /// </summary>
        LocalOnly
    };

    /// <summary>
    /// LocalSearchArgs derives from <see cref="GSearch.SearchArgs"/> and implements
    /// local search-specific arguments.
    /// <example>
    /// The following code illustrates how to create and use the LocalSearchArgs class:
    /// <code>
    /// GLocalSearch gls = new GLocalSearch();
    /// LocalSearchArgs lsa = new LocalSearchArgs();
    /// lsa.Terms = "Thai Restaurant";
    /// GLocalResponse resp = gls.Search(lsa);
    /// </code>
    /// </example>
    /// </summary>
    public class LocalSearchArgs : SearchArgs
    {
        /// <summary>
        /// Default constructor for the LocalSearchArgs class
        /// </summary>
        public LocalSearchArgs()
        {
            Results = SearchLocalResults.LocalOnly;
        }

        /// <summary>
        /// ToQueryString overrides the base class version. It calls the
        /// base class version to get the root of the query string, and then
        /// builds the rest of the parameters.
        /// </summary>
        /// <returns>A System.String containing the complete set of querystring
        /// arguments for the specified search.</returns>
        public override string ToQueryString()
        {
            string qry = base.ToQueryString();

            if ( null != SearchCenter )
                qry += "&sll=" 
                + SearchCenter.Latitude.ToString(CultureInfo.InvariantCulture.NumberFormat) 
                + ","
                + SearchCenter.Longitude.ToString(CultureInfo.InvariantCulture.NumberFormat);
            if ( null != Bounding )
                qry += "&sspn=" 
                + Bounding.Term1.ToString(CultureInfo.InvariantCulture.NumberFormat) 
                + ","
                + Bounding.Term2.ToString(CultureInfo.InvariantCulture.NumberFormat);
            qry += "&mrt=" + ArgStrings.lrtmap[Convert.ToInt32(Results)];

            return qry;
        }

        /// <summary>
        /// A <see cref="GSearch.Coordinate"/> containing the latitude and
        /// longitude around which the search should be centered, in
        /// decimal degrees.
        /// </summary>
        public Coordinate SearchCenter { get; set; }
        
        /// <summary>
        /// A <see cref="GSearch.BoundingRect"/> containing the terms that
        /// describe a bounding rectangle within which the search should
        /// take place. See Google API docs for more information.
        /// </summary>
        public BoundingRect Bounding { get; set; }
        
        /// <summary>
        /// A value from the <see cref="GSearch.SearchLocalResults"/> enum
        /// giving the type of results desired from the search.
        /// </summary>
        public SearchLocalResults Results { get; set; }
        
    }

    /// <summary>
    /// The GLocalSearch class provides an interface for searching local businesses
    /// and people using Google. It derives from <see cref="GSearch.GSearchCore"/>
    /// which does a large part of the work.
    /// <example>
    /// The following example shows how to use the GLocalSearch class to search
    /// Google Local:
    /// <code>
    /// GLocalSearch gls = new GLocalSearch();
    /// LocalSearchArgs lsa = new LocalSearchArgs();
    /// lsa.Terms = "Thai restaurant";
    /// GLocalResponse resp = gls.Search(lsa);
    /// if ( resp.Response.Results.Length > 0 )
    ///     // process results
    /// </code>
    /// </example>
    /// </summary>
    public class GLocalSearch : GSearchCore
    {
#if (!SILVERLIGHT)
        /// <summary>
        /// Performs a syncronous search for local information using the
        /// supplied arguments. The calling thread blocks until results are
        /// returned or an exception is thrown. This method is not available in the
        /// Silverlight version of the libraries.
        /// </summary>
        /// <param name="args"><see cref="GSearch.LocalSearchArgs"/> containing the search arguments</param>
        /// <returns><see cref="GSearch.GLocalResponse"/> containing the results</returns>
        /// <exception cref="Exception">if an error occurs during the search</exception>
        public GLocalResponse Search(LocalSearchArgs args)
        {
            return (GLocalResponse)base.Search(args);
        }
#endif

        /// <summary>
        /// Peforms an asyncronous search for local info using the supplied arguments. The
        /// calling thread returns immediately, and results or errors are indicated by
        /// raising the SearchFailed, SearchComplete, or SearchProgressChanged events.
        /// Results are returned on the UI thread.
        /// </summary>
        /// <param name="args"><see cref="GSearch.LocalSearchArgs"/> containing the search arguments</param>
        /// <exception cref="Exception">if an error occurs during the search</exception>
        public void SearchAsync(LocalSearchArgs args)
        {
            base.SearchAsync(args);
        }

        /// <summary>
        /// This event is raised when the search is complete and the results are
        /// ready to be returned to the client.
        /// </summary>
        public event EventHandler<LocalSearchEventArgs> SearchComplete;

        /// <summary>
        /// This event is raised when the search fails for some reason. The event
        /// arguments Ex property will reference the exception.
        /// </summary>
        public event EventHandler<LocalSearchEventArgs> SearchFailed;

        /// <summary>
        /// This event is raised when key steps in the search process are completed.
        /// See the SearchStatus enumeration for possible values for the 
        /// LocalSearchEventArgs.Status parameter.
        /// <seealso cref="GSearch.SearchStatus"/>
        /// </summary>
        public event EventHandler<LocalSearchEventArgs> SearchProgressChanged;

        /// <summary>
        /// A System.String containing a complete uri for the local search service
        /// </summary>
        public override string ServiceURI
        {
            get { return base.ServiceURI + LocalResources.ServiceURI; }
        }

        /// <summary>
        /// Called by GSearchCore to raise the SearchComplete event
        /// </summary>
        /// <param name="e"><see cref="SearchEventArgs"/> containing 
        /// information about the event</param>
        protected override void OnSearchComplete(SearchEventArgs e)
        {
            EventHandler<LocalSearchEventArgs> ev = SearchComplete;
            if (null != ev)
            {
                LocalSearchEventArgs ie = new LocalSearchEventArgs(e);
                ev(this, ie);
            }
        }

        /// <summary>
        /// Called by GSearchCore to raise the SearchFailed event
        /// </summary>
        /// <param name="e"><see cref="SearchEventArgs"/> containing 
        /// information about the event</param>
        protected override void OnSearchFailed(SearchEventArgs e)
        {
            EventHandler<LocalSearchEventArgs> ev = SearchFailed;
            if (null != ev)
            {
                LocalSearchEventArgs ie = new LocalSearchEventArgs(e);
                ev(this, ie);
            }
        }

        /// <summary>
        /// Called by GSearchCore to raise the SearchProgressChanged event
        /// </summary>
        /// <param name="e"><see cref="SearchEventArgs"/> containing 
        /// information about the event</param>
        protected override void OnSearchProgressChanged(SearchEventArgs e)
        {
            EventHandler<LocalSearchEventArgs> ev = SearchProgressChanged;
            if (null != ev)
            {
                LocalSearchEventArgs ie = new LocalSearchEventArgs(e);
                ev(this, ie);
            }
        }

        /// <summary>
        /// Deserialize is called from the base when the search returns a valid
        /// result stream, it refers the call to the base class DeserializeImpl
        /// which actually does the work of deserialization.
        /// </summary>
        /// <param name="result">Stream result passed from the base class</param>
        /// <returns>Instance of <see cref="GSearch.GLocalResponse"/> as GResponse</returns>
        protected override GResponse Deserialize(System.IO.Stream result)
        {
            return DeserializeImpl<GLocalResponse>(result);
        }
    }
}
